package com.soup.lab.third.lock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.params.SetParams;

import java.util.Objects;

/**
 * @ClassName RedisLock
 * @Description: Redis分布式锁实现
 * @Author gaoxiang
 * @Date 2020/8/14 9:32
 * @Version V1.0
 **/
public class RedisLock implements Lock {
    private static final Logger LOGGER = LoggerFactory.getLogger(RedisLock.class);
    // 默认过期时间为20s
    private final int expire;
    // 默认重试时间为100ms
    private static final long DEFAULT_RETRY_INTERVAL = 100;

    private static final int DEFAULT_EXPIRE = 20;

    private final JedisCluster jedisCluster;

    public RedisLock(JedisCluster jedisCluster) {
        this(jedisCluster, DEFAULT_EXPIRE);
    }

    public RedisLock(JedisCluster jedisCluster, int expire) {
        this.jedisCluster = jedisCluster;
        this.expire = expire;
    }

    @Override
    public boolean tryLock(String key, String value, long timeout, long retry) {
        LOGGER.debug("尝试获取分布式锁. key: {}, value: {}.", key, value);
        if(timeout > 0) {
            long limit = System.currentTimeMillis() + timeout;
            if(retry > timeout || retry < 0) {
                retry = timeout / 2;
            }
            while (!tryLock(key, value)) {
                if (System.currentTimeMillis() > limit) {
                    return false;
                }
                try {
                    Thread.sleep(retry);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } else {
            while (!tryLock(key, value)) {
                try {
                    Thread.sleep(retry);
                    LOGGER.debug("重试获取分布式锁. key: {}, value: {}.", key, value);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        LOGGER.debug("获取分布式锁成功. key: {}, value: {}.", key, value);
        return true;
    }

    @Override
    public boolean tryLock(String key, String value) {
        try {
            String str = jedisCluster.set(key, value, SetParams.setParams().nx().ex((long) expire));
            return "ok".equalsIgnoreCase(str);
        } catch (Exception e) {
            LOGGER.error("【获取Redis分布式锁异常】: {}", e);
        }
        return false;
    }

    @Override
    public boolean lock(String key, String value) {
        return tryLock(key, value, -1, DEFAULT_RETRY_INTERVAL);
    }

    @Override
    public boolean unlock(String key, String value) {
        LOGGER.debug("释放分布式锁. key: {}, value: {}.", key, value);
        try {
            //lua脚本原子操作判断是否是自己的锁：
            // if redis.call('get',#{key})==#{value} then return redis.cal('del',#{key}) else return 0 end
            String script = String.format("if redis.call('get','%s')=='%s' then return redis.call('del','%s') else return 0 end", key, value, key);
            return "1".equals(Objects.toString(jedisCluster.eval(script, key)));
        } catch (Exception e) {
            LOGGER.error("【释放Redis分布式锁异常】: {}", e);
        }
        LOGGER.debug("释放分布式锁失败. key: {}, value: {}.", key, value);
        return false;
    }
}
